package com.foreveross.bsl.push.application.impl;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;

import com.foreveross.bsl.common.utils.mapper.BeanMapper;
import com.foreveross.bsl.common.utils.web.QueryOrder;
import com.foreveross.bsl.common.utils.web.QuerySpecs;
import com.foreveross.bsl.push.application.CheckinEvent;
import com.foreveross.bsl.push.application.CheckinMgmtService;
import com.foreveross.bsl.push.application.PushModuleException;
import com.foreveross.bsl.push.application.TagSummaryService;
import com.foreveross.bsl.push.application.UpdateTagEvent;
import com.foreveross.bsl.push.application.vo.DeviceCheckinVo;
import com.foreveross.bsl.push.application.vo.MessageParams;
import com.foreveross.bsl.push.application.vo.ReceiverTypeEnumVo;
import com.foreveross.bsl.push.application.vo.TagEntryVo;
import com.foreveross.bsl.push.domain.entity.DeviceCheckin;
import com.foreveross.bsl.push.domain.entity.TagEntry;
import com.foreveross.bsl.push.domain.entity.TagsHelper;
import com.foreveross.bsl.push.repository.DeviceCheckinRepository;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

@Named("defaultCheckinMgmtService")
public class DefaultCheckinMgmtService implements CheckinMgmtService {

	private final static Logger log = LoggerFactory.getLogger(DefaultCheckinMgmtService.class);

	@Inject
	private EventBus eventBus;
	
	@Inject
	private TagSummaryService tagSummaryService;

	
	private final DeviceCheckinRepository getDeviceCheckinRepository(){
		return new DeviceCheckin().getEntityRepository(DeviceCheckinRepository.class);
	}
	
	@Override
	public void remove(String checkinId) {
		this.getDeviceCheckinRepository().delete(checkinId);
	}
	
	@Override
	public int clearToken(String token) {
		return this.getDeviceCheckinRepository().clearToken(token);
	}

	@Override
	public Page<DeviceCheckinVo> findPageBy(int pageNumber, int pageSize, QuerySpecs querySpecs) {
		if(querySpecs==null){
			querySpecs=new QuerySpecs();
		}
		querySpecs.setOrder("chechin_time", QueryOrder.Direction.DESC);

		pageNumber = pageNumber <= 0 ? 0 : pageNumber - 1;
		pageSize = pageSize <= 0 ? 10 : pageSize;
		Pageable pageable = new PageRequest(pageNumber, pageSize);

		Page<DeviceCheckin> page = this.getDeviceCheckinRepository().findByQuerySpecs(querySpecs, pageable);
		List<DeviceCheckinVo> list = BeanMapper.mapList(page.getContent(), DeviceCheckinVo.class);
		return new PageImpl<DeviceCheckinVo>(list, pageable, page.getTotalElements());
	}

	private String transferPropertyName(ReceiverTypeEnumVo receiverType) {
		String propertyName = null;
		if (ReceiverTypeEnumVo.ALIAS.equals(receiverType)) {
			propertyName = "alias";
		} else if (ReceiverTypeEnumVo.APP_ID.equals(receiverType)) {
			propertyName = "appId";
		} else if (ReceiverTypeEnumVo.DEVICE_ID.equals(receiverType)) {
			propertyName = "deviceId";
		} else if (ReceiverTypeEnumVo.TAG.equals(receiverType)) {
			propertyName = "tags";
		} else {
			throw new PushModuleException("invalid receiverType: " + receiverType);
		}
		return propertyName;
	}

	private Page<DeviceCheckin> findDeviceCheckinsByMsgParams(MessageParams msgParams, Pageable pageable){
		final DeviceCheckinRepository repo=this.getDeviceCheckinRepository();
		String propertyName = transferPropertyName(msgParams.getReceiverType());
		String value=msgParams.getReceiverValue();
		String[] values=null;
		Page<DeviceCheckin> page = null;
		
		Criteria crit=null;
		if(msgParams.isByApp()){
			crit=Criteria.where("appId").is(msgParams.getAppId());
		}
		else if(msgParams.isByAlias() || msgParams.isByDevice()){
			if(StringUtils.isBlank(value)){
				throw new IllegalArgumentException("按设备ID或别名方式推送必须设置设备ID或别名");
			}
			crit=Criteria.where("appId").is(msgParams.getAppId());
			values=StringUtils.split(value, ',');
			if(values.length==1){
				crit.and(propertyName).is(values[0]);			
			}
			else{
				crit.and(propertyName).in(Arrays.asList(values));
			}
		}
		page=repo.findByQuery(Query.query(crit), pageable);
		return page;
	}
	
	@Override
	public boolean existsTarget(MessageParams msgParams) {
		final DeviceCheckinRepository repo=this.getDeviceCheckinRepository();
		if (msgParams.isByTag()) {
			Assert.notEmpty(msgParams.getTags());
			return repo.existsByTags(msgParams.getTags(), msgParams.getAppId());
		} else {
			Pageable pageable=new PageRequest(0, 1);
			Page<DeviceCheckin> page = this.findDeviceCheckinsByMsgParams(msgParams, pageable);
			return page != null && page.getTotalElements() > 0;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.push.application.CheckinMgmtService#findTarget(com
	 * .foreveross.bsl.push.application.vo.MessageParams,
	 * org.springframework.data.domain.Pageable)
	 */
	@Override
	public Page<DeviceCheckinVo> findTargets(int pageNumber, int pageSize, MessageParams msgParams) {
		pageNumber = pageNumber <= 0 ? 0 : pageNumber - 1;
		pageSize = pageSize <= 0 ? 10 : pageSize;
		Pageable pageable=new PageRequest(pageNumber, pageSize);
		final DeviceCheckinRepository repo=this.getDeviceCheckinRepository();
		if (msgParams.isByTag()) {
			Assert.notEmpty(msgParams.getTags());
			Page<DeviceCheckin> page = repo.findByTags(msgParams.getTags(), msgParams.getAppId(), pageable);
			List<DeviceCheckinVo> list = BeanMapper.mapList(page.getContent(), DeviceCheckinVo.class);
			return new PageImpl<DeviceCheckinVo>(list, pageable, page.getTotalElements());
		} else {
			Page<DeviceCheckin> page = this.findDeviceCheckinsByMsgParams(msgParams, pageable);
			List<DeviceCheckinVo> list = BeanMapper.mapList(page.getContent(), DeviceCheckinVo.class);
			return new PageImpl<DeviceCheckinVo>(list, pageable, page.getTotalElements());
		}
	}

	@PostConstruct
	public void init() {
		eventBus.register(this);
		log.info("注册CheckinEvent,UpdateTagEvent事件监听器:{}", this);
	}

	/**
	 * 处理CheckinEvent事件
	 * 
	 * @param evt
	 */
	@Subscribe
	public void handleCheckinEvent(CheckinEvent evt) {
		DsRouterHelper.setCurrentDsRouter(evt.getDsRouter());
		// 更新标签摘要
		if (CheckinEvent.Action.Checkin.equals(evt.getAction())) {
			DeviceCheckinVo chki = evt.getCheckinInfo();
			if (chki.getTags() != null) {
				List<TagEntryVo> tagList=Arrays.asList(chki.getTags());
				this.updateTagSummary(chki.getAppId(), chki.getDeviceId(), tagList);
			}
		}
	}

	@Subscribe
	public void handleUpdateTagEvent(UpdateTagEvent evt) {
		DsRouterHelper.setCurrentDsRouter(evt.getDsRouter());
		this.updateTagSummary(evt.getAppId(), evt.getDeviceId(), evt.getTags());
	}

	protected void updateTagSummary(String appId, String deviceId, Collection<TagEntryVo> tags) {
		Collection<TagEntry> list=BeanMapper.mapList(tags, TagEntry.class); 
		TagsHelper th=new TagsHelper(list);
		String[] keys=th.getTagKeys();
		for(String key : keys){
			String[] values=th.getTags(key);
			this.tagSummaryService.updateTagSummary(appId, key, values);
		}
	}
	
	protected void updateTagSummary(String appId, String deviceId, Map<String, String> tags) {
		TagsHelper th=new TagsHelper(tags);
		String[] keys=th.getTagKeys();
		for(String key : keys){
			String[] values=th.getTags(key);
			this.tagSummaryService.updateTagSummary(appId, key, values);
		}
	}
	
}
